﻿using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.IO;
Console.WriteLine("正在准备测试数据");

ReadOnlyMemory<byte> dataToSend;
string path = Path.Combine(Directory.GetCurrentDirectory(), "pg3200.txt");
if (File.Exists(path))
{
    dataToSend = await File.ReadAllBytesAsync(path);    
}
else
{

    using (var hc = new HttpClient())
    {
        dataToSend = await hc.GetByteArrayAsync("https://www.gutenberg.org/cache/epub/3200/pg3200.txt");
    }
    await File.WriteAllBytesAsync(path, dataToSend.ToArray());
}

Console.WriteLine("开始测试");
Memory<byte> receiveBuffer = new byte[dataToSend.Length];

foreach (bool compressed in new[] { false, true })
{
    using var listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    listener.Bind(new IPEndPoint(IPAddress.Loopback, 0));
    listener.Listen();

    using var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    client.Connect(listener.LocalEndPoint);
    using Socket server = listener.Accept();

    using var clientStream = new PassthroughTrackingStream(new NetworkStream(client, ownsSocket: true));
    using var clientWS = WebSocket.CreateFromStream(clientStream, new WebSocketCreationOptions { IsServer = false, DangerousDeflateOptions = compressed ? new WebSocketDeflateOptions() : null });
    using var serverWS = WebSocket.CreateFromStream(new NetworkStream(server, ownsSocket: true), new WebSocketCreationOptions { IsServer = true, DangerousDeflateOptions = compressed ? new WebSocketDeflateOptions() : null });

    var sw = new Stopwatch();
    for (int trial = 0; trial < 50; trial++)
    {
        long before = clientStream.BytesRead;
        sw.Restart();

        await serverWS.SendAsync(dataToSend, WebSocketMessageType.Binary, true, default);
        while (!(await clientWS.ReceiveAsync(receiveBuffer, default)).EndOfMessage) ;

        sw.Stop();
        Console.WriteLine($"Compressed: {compressed,5} Bytes: {clientStream.BytesRead - before,10:N0} Time: {sw.ElapsedMilliseconds:N0}ms");
    }
}
Console.WriteLine("测试完毕！");
Console.ReadKey();
sealed class PassthroughTrackingStream : Stream
{
    private readonly Stream _stream;
    public long BytesRead;

    public PassthroughTrackingStream(Stream stream) => _stream = stream;

    public override bool CanWrite => true;
    public override bool CanRead => true;

    public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken)
    {
        int n = await _stream.ReadAsync(buffer, cancellationToken);
        BytesRead += n;
        return n;
    }

    public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken) =>
        _stream.WriteAsync(buffer, cancellationToken);

    protected override void Dispose(bool disposing) => _stream.Dispose();
    public override bool CanSeek => _stream.CanSeek;
    public override long Length => _stream.Length;
    public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
    public override void Flush() { _stream.Flush(); }
    public override int Read(byte[] buffer, int offset, int count) => _stream.Read(buffer, offset,count);
    public override long Seek(long offset, SeekOrigin origin) => _stream.Seek(offset, origin);
    public override void SetLength(long value) => _stream.SetLength(value);
    public override void Write(byte[] buffer, int offset, int count) => _stream.Write(buffer,offset,count);
}